package io.youngledo.usb.storage.impl;

import io.youngledo.usb.storage.*;
import io.youngledo.usb.exception.StorageOperationException;
import io.youngledo.usb.config.TimeoutConfig;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;

import java.io.*;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

/// 腾讯云COS存储服务适配器，[地域和访问域名](https://cloud.tencent.com/document/product/436/6224)
///
public class TencentStorageService extends AbstractObjectStorageService {

    private COSClient cosClient;

    @Override
    public void connect(StorageConfig config) {
        if (config.type() != StorageType.TENCENT_COS) {
            throw new IllegalArgumentException("Invalid storage type for Tencent COS service: " + config.type());
        }

        this.config = config;

        // 创建COS凭证
        COSCredentials cred = new BasicCOSCredentials(config.accessKey(), config.secretKey());

        // 设置bucket的地域
        Region region = new Region(config.region());
        ClientConfig clientConfig = new ClientConfig(region);

        // 设置统一的超时配置
        clientConfig.setConnectionTimeout(TimeoutConfig.CONNECTION_TIMEOUT_MS);
        clientConfig.setSocketTimeout(TimeoutConfig.SOCKET_TIMEOUT_MS);

        // 设置使用HTTPS
        if (config.useSSL()) {
            clientConfig.setHttpProtocol(com.qcloud.cos.http.HttpProtocol.https);
        } else {
            clientConfig.setHttpProtocol(com.qcloud.cos.http.HttpProtocol.http);
        }

        // 创建COS客户端
        this.cosClient = new COSClient(cred, clientConfig);

        // 测试连接 - 尝试列出存储桶
        cosClient.listBuckets();
        this.connected = true;
    }

    @Override
    protected void performDisconnect() {
        if (cosClient != null) {
            cosClient.shutdown();
            cosClient = null;
        }
    }

    @Override
    public List<StorageBucket> listBuckets() {
        checkConnection();
        return cosClient.listBuckets()
                .parallelStream()
                .map(bucket -> StorageBucket.of(
                        bucket.getName(),
                        bucket.getCreationDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
                )).toList();
    }

    @Override
    public void createBucket(String bucketName) {
        checkConnection();
        try {
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            cosClient.createBucket(createBucketRequest);
        } catch (CosServiceException e) {
            throw StorageOperationException.operationFailed("Failed to create bucket: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            throw StorageOperationException.operationFailed("Failed to create bucket: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteBucket(String bucketName) {
        checkConnection();
        try {
            DeleteBucketRequest deleteBucketRequest = new DeleteBucketRequest(bucketName);
            cosClient.deleteBucket(deleteBucketRequest);
        } catch (CosClientException e) {
            throw StorageOperationException.bucketNotFound(bucketName);
        }
    }

    @Override
    public List<StorageObject> listObjects(String bucketName, String prefix, boolean recursive) {
        checkConnection();
        List<StorageObject> objects = new ArrayList<>();
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(bucketName);
        listObjectsRequest.setPrefix(prefix);

        if (!recursive) {
            listObjectsRequest.setDelimiter("/");
        }

        ObjectListing objectListing = cosClient.listObjects(listObjectsRequest);

        // 处理文件对象
        objects.addAll(ObjectStorageHelper.processObjects(
                objectListing.getObjectSummaries(),
                prefix,
                COSObjectSummary::getKey,
                (summary, key, displayName) -> new StorageObject(
                        key,
                        displayName,
                        summary.getSize(),
                        summary.getLastModified().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
                        false,
                        null
                )
        ));

        // 处理目录（如果不是递归模式）
        if (!recursive) {
            objects.addAll(ObjectStorageHelper.processCommonPrefixes(
                    objectListing.getCommonPrefixes(),
                    prefix,
                    false  // 不移除末尾的斜杠
            ));
        }

        return objects;
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, File file) {
        checkConnection();
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, file);
            cosClient.putObject(putObjectRequest);
        } catch (CosServiceException e) {
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, InputStream inputStream, long contentLength) {
        checkConnection();
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(contentLength);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, inputStream, metadata);
            cosClient.putObject(putObjectRequest);
        } catch (CosServiceException e) {
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream downloadFile(String bucketName, String objectKey) {
        checkConnection();
        try {
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectKey);
            COSObject cosObject = cosClient.getObject(getObjectRequest);
            return cosObject.getObjectContent();
        } catch (CosServiceException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        } catch (CosClientException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void downloadFile(String bucketName, String objectKey, File localFile) {
        checkConnection();
        try {
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectKey);
            cosClient.getObject(getObjectRequest, localFile);
        } catch (CosServiceException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        } catch (CosClientException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObject(String bucketName, String objectKey) {
        checkConnection();
        try {
            DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucketName, objectKey);
            cosClient.deleteObject(deleteObjectRequest);
        } catch (CosServiceException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        } catch (CosClientException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObjects(String bucketName, List<String> objectKeys) {
        checkConnection();
        try {
            DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName);
            List<DeleteObjectsRequest.KeyVersion> keyVersions = new ArrayList<>();
            for (String key : objectKeys) {
                keyVersions.add(new DeleteObjectsRequest.KeyVersion(key));
            }
            deleteObjectsRequest.setKeys(keyVersions);
            cosClient.deleteObjects(deleteObjectsRequest);
        } catch (CosServiceException e) {
            throw StorageOperationException.operationFailed("Failed to delete objects: " + e.getErrorMessage(), e);
        } catch (CosClientException e) {
            throw StorageOperationException.operationFailed("Failed to delete objects: " + e.getMessage(), e);
        }
    }


    @Override
    public StorageObject getObjectInfo(String bucketName, String objectKey) {
        checkConnection();
        try {
            GetObjectMetadataRequest getMetadataRequest = new GetObjectMetadataRequest(bucketName, objectKey);
            ObjectMetadata metadata = cosClient.getObjectMetadata(getMetadataRequest);

            return new StorageObject(
                    objectKey,
                    objectKey.substring(objectKey.lastIndexOf('/') + 1),
                    metadata.getContentLength(),
                    metadata.getLastModified().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
                    objectKey.endsWith("/"),
                    null
            );
        } catch (CosServiceException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        } catch (CosClientException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public StorageType getStorageType() {
        return StorageType.TENCENT_COS;
    }

}